package com.android.a8pool;
import java.util.Random;
import java.util.Vector;

import com.akjava.android.box2d.BodyInfo;
import com.akjava.android.box2d.NDKBox2dControler;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.os.SystemClock;
import android.view.View;

public class BillardWorld extends View{

	public static final int MAX_BALLS = 50;
	public static final float MIN_LINEAR_VELOCITY = 0.1f;
	public static final float POWER_MULTI = 10.0f;

	public long targetFPS = 40;
	public long targetTimeStep = 1000 / targetFPS;
	public long timeStep = 1000 / targetFPS;
	public int iterations = 10;
	BodyInfo info = new BodyInfo();

	private int[] bodies;
	private int count = 0;
	private int nMovingBalls = 0;

	private boolean bWaitRedraw = false;

	long lastTime = 0;

	public int World_W,World_H;

	private Paint paint;

	private float radius=1.0f;

	public boolean drawImpulse;

	Random rnd;

	int ball1= -1;
	int brique;

	public Vec2D gravity;
	public Vec2D impulse;

	Vector<Ball> newBalls = new Vector();

	private NDKBox2dControler box2dControler=new NDKBox2dControler();

	public class Vec2D {
		float x, y, x1, y1;

		public Vec2D(float x, float y, float x1, float y1) {
			this.x=x;
			this.y=y;
			this.x1=x1;
			this.y1=y1;
		}

		public float getXcomp(){
			return x1-x;
		}

		public float getYcomp(){
			return y1-y;
		}
	}

	private class Ball {
		float x, y, radius, density, restitution;

		public Ball(float x, float y, float radius, float density, float restitution) {
			this.x=x;
			this.y=y;
			this.radius=radius;
			this.density=density;
			this.restitution=restitution;
		}
	}

	public BillardWorld(Context context,int W,int H) {
		super(context);

		bodies=new int[MAX_BALLS];
		for(int j = 0;j<MAX_BALLS;j++) {
			bodies[j]=0;
		}
		rnd = new Random(SystemClock.uptimeMillis());
		World_W=W;
		World_H=H;

		gravity = new Vec2D(0.0f, 0.0f, 0.0f, 0.0f);
		impulse = new Vec2D(0.0f, 0.0f, 0.0f, 0.0f);
		// Step 1: Create Physics World Boundaries

		box2dControler.createWorld(-5, -5, (World_W + 50)/10, (World_H + 50)/10,gravity.getXcomp(), gravity.getYcomp());

		// Step 3:

		//Create Ground Box :
		int down=box2dControler.createBox(-1.0f, -1.0f,((float)World_W+20)/10, 1.0f);

		// up :
		int up=box2dControler.createBox(-1.0f, (float)World_H/10,((float)World_W+20)/10, 1.0f);


		// left :
		int left=box2dControler.createBox(-1.0f, 0.0f, 1.0f, (float)World_H/10);

		// right :
		int right=box2dControler.createBox((float)World_W/10, 0.0f, 1.0f, (float)World_H/10);


		brique=box2dControler.createBox((float)World_W/20, (float) World_H/20, 1.0f, 1.0f);

		paint=new Paint();
		paint.setStyle(Style.FILL);
		paint.setColor(Color.RED);
	}
	public void addBallXY(float x, float y, float radius) {
		Ball newBall = new Ball(x, y, radius, 1.0f, 0.8f);
		newBalls.add(newBall);
	}

	public void addBallRand() {
		Ball newBall = new Ball(
				((float) radius*2+rnd.nextInt( (int)(World_W-radius*4) ))/10, 
				((float)2*radius+ rnd.nextInt( (int)(World_H-radius*4) ))/10, 
				(float) radius,
				1.0f, 
				0.8f);
		newBalls.add(newBall);
	}

	public void addBall(Ball newBall) {
		// Create Dynamic Body
		int ball=box2dControler.createDampingCircle(
				newBall.x,
				newBall.y,
				newBall.radius,
				1.0f, 
				0.9f,
				0.5f);
		if (bodies[count]>0){
			box2dControler.destroyBody(bodies[count]);
		}

		bodies[count] = ball;

		if (ball1<0) ball1 = count;

		// Increase Counter
		count = (count + 1) % MAX_BALLS;
	}

	public void update() {
		Ball newBall;
		float velocity;
		synchronized(box2dControler){
			//adding new balls
			while (newBalls.size()>0){
				newBall = newBalls.remove(0);
				addBall(newBall);
			}

			if (lastTime>0){
				timeStep = SystemClock.uptimeMillis()-lastTime;
			}

			if (timeStep>=targetTimeStep){
				lastTime = SystemClock.uptimeMillis();
				box2dControler.step( (float)timeStep/1000, iterations);

				//we stop ball where velocity < linearVelocityLimit
				nMovingBalls = 0;
				for(int j = 0;j<MAX_BALLS;j++) {
					if (bodies[j]>0){
						box2dControler.getLinerVelocity(info, bodies[j]);
						velocity = (info.linearX * info.linearX + info.linearY * info.linearY);
						if (velocity>0 && velocity <= MIN_LINEAR_VELOCITY*MIN_LINEAR_VELOCITY){
							box2dControler.setBodyLinearVelocity(bodies[j], 0.0f, 0.0f);
							box2dControler.setBodyAngularVelocity(bodies[j], 0.0f);
						}
						else if (velocity>0)
							nMovingBalls++;
					}
				}

				postInvalidate();

			}
		}

	}  
	@Override
	protected void onDraw(Canvas canvas) {
		synchronized (box2dControler)
		{
			// draw balls
			int drawn= 0;
			float vx=-1, vy=-1;
			paint.setColor(Color.RED);
			for(int j = 0;j<MAX_BALLS;j++) {
				if (bodies[j]>0){
					box2dControler.getBodyInfo(info, bodies[j]);
					vx = info.getX()*10;
					vy = World_H-info.getY()*10;
					if (j==0) paint.setColor(Color.WHITE);
					else if (j<8) paint.setColor(Color.RED);
					else paint.setColor(Color.YELLOW);
					canvas.drawCircle(vx,vy, radius*10, paint);
					paint.setColor(Color.WHITE);
					canvas.drawLine(vx,vy,vx + (float) (radius*10*Math.cos(info.angle)),vy + (float) (radius*10*Math.sin(info.angle)), paint);
					drawn++;
				}

			}
			box2dControler.getBodyInfo(info, brique);
			canvas.drawRect(info.getX()*10-5, info.getY()*10-15, info.getX()*10+5, info.getY()*10-5, paint);


			if (drawImpulse){
				paint.setColor(Color.BLUE);
				box2dControler.getBodyInfo(info, bodies[ball1]);
				canvas.drawLine(info.getX()*10, World_H-info.getY()*10, impulse.x, World_H-impulse.y, paint);
			}

			paint.setColor(Color.WHITE);
			canvas.drawText("balls = " + drawn, 10, 12, paint);
			canvas.drawText("timeStep = " + timeStep, 10, 24, paint);
			canvas.drawText("FPS = " + 1000/Math.max(1, timeStep), 10, 36, paint);
			canvas.drawText("Moving balls =" + nMovingBalls, 10, 48, paint);
		}
	}

	public void impulse1(float x, float y){
		if (ball1>=0)
			synchronized (box2dControler)
			{
				box2dControler.getBodyInfo(info, bodies[ball1]);
				box2dControler.ApplyImpulse(bodies[ball1], (info.getX()-x/10)*POWER_MULTI, (info.getY()-y/10)*POWER_MULTI, info.getX(), info.getY());
			}
	}
} 